#include "osdPlugin.h"
#include <freetype/freetype.h>
#include <ft2build.h>
#include <stdio.h>
#include <cmath>
#include <codecvt>
#include <memory>
#include <mutex>
#include <opencv2/opencv.hpp>
#include <string>
#include <vector>
#include "boxInfo.h"
#include "fontdata.h"
#include "lyn_plugin_dev.h"

// const static std::wstring PLATE_DATA =
//     L"京沪津渝冀晋蒙辽吉黑苏浙皖闽赣鲁豫鄂湘粤桂琼川贵云藏陕甘青宁新0123456789A"
//     L"BCDEFGHJKLMNPQRSTUVWXYZ港学使警澳挂军北南广沈兰成济海民航空";

std::wstring UTF8ToWstring(char *text) {
  static std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
  std::wstring wideString =
      converter.from_bytes(text);  // 将UTF-8字符串转换为宽字符串
  return wideString;
}

// 目标信息
// typedef struct {
//   // 坐标信息
//   int left;
//   int top;
//   int width;
//   int height;
//   // 文本信息
//   char info[128];
// } TARGET_INFO;

// struct TextBitMap {
//   uint8_t *buffer = nullptr;
//   int w = 0;
//   int h = 0;
//   // char ch;
//   // lynFontSize fontSize;
// };

// void setFontSize(FT_Face *ftFace, int fontSize) {
//   FT_Set_Char_Size(*ftFace, 0, fontSize * 64, 0, 96);
//   FT_Set_Pixel_Sizes(*ftFace, 0, fontSize);
// }

// int drawText(uint32_t startX, uint32_t startY, uint32_t imgW, uint32_t imgH,
//              lynPixelFormat_t imgFmt, unsigned char *imgData,
//              char vIndexs[20]) {
//   static bool s_init = false;
//   // static int s_fontSize = 0;
//   static std::mutex s_drawTextMux;
//   static FT_Library ftLib;
//   static FT_Face ftFace;
//   static std::vector<TextBitMap> s_textVec;

//   int ret;
//   if (imgData == nullptr) {
//     return -1;
//   }

//   if (!s_init) {
//     std::unique_lock<std::mutex> lock(s_drawTextMux);

//     ret = FT_Init_FreeType(&ftLib);
//     ret = FT_New_Memory_Face(ftLib, (const unsigned char *)fontData,
//                              sizeof(fontData), 0, &ftFace);
//     if (ret != 0) {
//       return -2;
//     }

//     FT_Select_Charmap(ftFace, FT_ENCODING_UNICODE);
//     setFontSize(&ftFace, FONT_SIZE_24);

//     for (int i = 0; i < PLATE_DATA.length(); i++) {
//       if (i == 31) {
//         setFontSize(&ftFace, 30);
//       } else if (i == 65) {
//         setFontSize(&ftFace, FONT_SIZE_24);
//       }

//       wchar_t ch = PLATE_DATA.at(i);
//       auto index = FT_Get_Char_Index(ftFace, ch);
//       FT_Load_Glyph(ftFace, index, FT_LOAD_DEFAULT);

//       //
//       FT_Render_Glyph(ftFace->glyph, FT_RENDER_MODE_NORMAL);
//       TextBitMap bitMap;
//       bitMap.w = ftFace->glyph->bitmap.width;
//       bitMap.h = ftFace->glyph->bitmap.rows;
//       bitMap.buffer = (uint8_t *)malloc(bitMap.w * bitMap.h);
//       // bitMap.fontSize = fontSize;

//       memcpy(bitMap.buffer, ftFace->glyph->bitmap.buffer, bitMap.w *
//       bitMap.h); s_textVec.emplace_back(bitMap);
//     }

//     s_init = true;
//   }

//   unsigned char *data_y = imgData;
//   unsigned char *data_u = imgData + imgW * imgH;
//   unsigned char *data_v = imgData + (imgW * imgH) * 5 / 4;

//   int pix_x_base = startX;
//   int pix_y_base = startY;

//   for (int k = 0; k < 20; ++k) {
//     int idx = vIndexs[k];
//     if (idx >= PLATE_DATA.length()) {
//       continue;
//     }

//     int w = s_textVec[idx].w;
//     int h = s_textVec[idx].h;
//     pix_y_base = startY - h;

//     unsigned char *data = s_textVec[idx].buffer;
//     for (int j = 0; j < h; ++j) {
//       for (int i = 0; i < w; ++i) {
//         int pix_x = pix_x_base + i;
//         int pix_y = pix_y_base + j;

//         if (pix_y < 0 || pix_y >= imgH || pix_x >= imgW || pix_x < 0) {
//           continue;
//         }

//         int a = data[j * w + i] >> 5;
//         data_y[pix_y * imgW + pix_x] =
//             data_y[pix_y * imgW + pix_x] * (8 - a) / 8 + (255 >> 3) * a;
//       }
//     }
//     pix_x_base += (w + 2);
//   }

//   return 0;
// }

// int osdDraw(OSD_PARAM_T *param) {
//   if (param->targetData == nullptr || param->targetNum == 0) {
//     return 0;
//   }

//   void *imgData = lynPluginGetVirtAddr(param->imgData);
//   if (imgData == nullptr) {
//     LOG_PLUGIN_E("get imgData addr error");
//     return -1;
//   }

//   TARGET_INFO *targetData =
//       (TARGET_INFO *)lynPluginGetVirtAddr(param->targetData);
//   if (targetData == nullptr) {
//     LOG_PLUGIN_E("get targetData addr error\n");
//     return -1;
//   }

//   for (int i = 0; i < param->targetNum; i++) {
//     lynDrawBoxAttr boxAttr;
//     TARGET_INFO *pInfo = targetData + i;

//     if (pInfo->width <= DRAW_THICK_2 * 2 || pInfo->height <= DRAW_THICK_2 *
//     2) {
//       continue;
//     }

//     if (pInfo->info[0] == 0xff && pInfo->info[1] == 0xfe) {
//       drawText(pInfo->left + 100, pInfo->top, param->imgW, param->imgH,
//                LYN_PIX_FMT_NV12, (unsigned char *)imgData, pInfo->info + 2);
//     } else {
//       boxAttr.startX = pInfo->left;
//       boxAttr.startY = pInfo->top;
//       boxAttr.boxW = pInfo->width;
//       boxAttr.boxH = pInfo->height;
//       boxAttr.imgW = param->imgW;
//       boxAttr.imgH = param->imgH;
//       boxAttr.thickness = DRAW_THICK_2;
//       boxAttr.color = DRAW_COLOR_BLUE;
//       boxAttr.imgFmt = LYN_PIX_FMT_NV12;
//       boxAttr.imgData = (unsigned char *)imgData;
//       int ret = lynPluginDrawBox(&boxAttr);
//       if (ret != 0) {
//         continue;
//       }

//       lynDrawTextAttr textAttr;
//       textAttr.startX = pInfo->left;
//       textAttr.startY = pInfo->top;
//       textAttr.imgW = param->imgW;
//       textAttr.imgH = param->imgH;
//       textAttr.imgFmt = LYN_PIX_FMT_NV12;
//       textAttr.fontSize = FONT_SIZE_36;
//       textAttr.imgData = (unsigned char *)imgData;
//       textAttr.text = pInfo->info;
//       lynPluginDrawText(&textAttr);
//     }
//   }

//   return 0;
// }

const std::vector<std::vector<unsigned int>> KPS_COLORS =
   {{0,   255, 0},
    {0,   255, 0},
    {0,   255, 0},
    {0,   255, 0},
    {0,   255, 0},
    {255, 128, 0},
    {255, 128, 0},
    {255, 128, 0},
    {255, 128, 0},
    {255, 128, 0},
    {255, 128, 0},
    {51,  153, 255},
    {51,  153, 255},
    {51,  153, 255},
    {51,  153, 255},
    {51,  153, 255},
    {51,  153, 255}};

const std::vector<std::vector<unsigned int>> SKELETON =
   {{16, 14},
    {14, 12},
    {17, 15},
    {15, 13},
    {12, 13},
    {6,  12},
    {7,  13},
    {6,  7},
    {6,  8},
    {7,  9},
    {8,  10},
    {9,  11},
    {2,  3},
    {1,  2},
    {1,  3},
    {2,  4},
    {3,  5},
    {4,  6},
    {5,  7}};

const std::vector<std::vector<unsigned int>> LIMB_COLORS =
   {{51,  153, 255},
    {51,  153, 255},
    {51,  153, 255},
    {51,  153, 255},
    {255, 51,  255},
    {255, 51,  255},
    {255, 51,  255},
    {255, 128, 0},
    {255, 128, 0},
    {255, 128, 0},
    {255, 128, 0},
    {255, 128, 0},
    {0,   255, 0},
    {0,   255, 0},
    {0,   255, 0},
    {0,   255, 0},
    {0,   255, 0},
    {0,   255, 0},
    {0,   255, 0}};

int poseDraw(POSE_PARAM_T *param) {
  if (param->poseInfo == nullptr) {
    return 0;
  }

  param->imgData = (unsigned char *)lynPluginGetVirtAddr(param->imgData);
  if (param->imgData == nullptr) {
    LOG_PLUGIN_E("get imgData addr error");
    return -1;
  }

  lynPoseInfo *poseInfo = (lynPoseInfo*)lynPluginGetVirtAddr(param->poseInfo); 
  if (poseInfo == nullptr) {
    LOG_PLUGIN_E("get poseInfo addr error\n");
    return -1;
  }

  if (poseInfo->boxesNum == 0) {
    return 0;
  }

  int bytesPerChannel = sizeof(unsigned char);
  int bytesPerRow = param->width * param->channels * bytesPerChannel;
  cv::Mat mat(param->height, param->width, CV_8UC(param->channels),
  param->imgData, bytesPerRow);

  for (int i = 0; i < poseInfo->boxesNum; i++) {
    lynPosePoint *pInfo = &poseInfo->points[i];
    auto kps = pInfo->kps;
    for (int k = 0; k < param->num_point + 2; k++){
      // 关键点绘制
      if (k < param->num_point){
          int kps_x = std::round(kps[k * 3]);
          int kps_y = std::round(kps[k * 3 + 1]);
          float kps_s = kps[k * 3 + 2];

          if (kps_s > param->_kpsVThrehold){
              cv::Scalar kps_color =
              cv::Scalar(KPS_COLORS[k][0],KPS_COLORS[k][1],KPS_COLORS[k][2]);
              cv::circle(mat, {kps_x, kps_y}, 5, kps_color, -1);
          }
      }

      auto &ske = SKELETON[k];
      int pos1_x = std::round(kps[(ske[0] -1) * 3]);
      int pos1_y = std::round(kps[(ske[0] -1) * 3 + 1]);

      int pos2_x = std::round(kps[(ske[1] -1) * 3]);
      int pos2_y = std::round(kps[(ske[1] -1) * 3 + 1]);

      float pos1_s = kps[(ske[0] -1) * 3 + 2];
      float pos2_s = kps[(ske[1] -1) * 3 + 2];

      if (pos1_s > param->_kpsVThrehold && pos2_s > param->_kpsVThrehold){
          cv::Scalar limb_color = cv::Scalar(LIMB_COLORS[k][0],
          LIMB_COLORS[k][1], LIMB_COLORS[k][2]); cv::line(mat, {pos1_x,
          pos1_y}, {pos2_x, pos2_y}, limb_color, 2);
      }
    }
  }
  return 0;
}

const std::vector<cv::Scalar> color80{
    cv::Scalar(128, 77, 207),  cv::Scalar(65, 32, 208),
    cv::Scalar(0, 224, 45),    cv::Scalar(3, 141, 219),
    cv::Scalar(80, 239, 253),  cv::Scalar(239, 184, 12),
    cv::Scalar(7, 144, 145),   cv::Scalar(161, 88, 57),
    cv::Scalar(0, 166, 46),    cv::Scalar(218, 113, 53),
    cv::Scalar(193, 33, 128),  cv::Scalar(190, 94, 113),
    cv::Scalar(113, 123, 232), cv::Scalar(69, 205, 80),
    cv::Scalar(18, 170, 49),   cv::Scalar(89, 51, 241),
    cv::Scalar(153, 191, 154), cv::Scalar(27, 26, 69),
    cv::Scalar(20, 186, 194),  cv::Scalar(210, 202, 167),
    cv::Scalar(196, 113, 204), cv::Scalar(9, 81, 88),
    cv::Scalar(191, 162, 67),  cv::Scalar(227, 73, 120),
    cv::Scalar(177, 31, 19),   cv::Scalar(133, 102, 137),
    cv::Scalar(146, 72, 97),   cv::Scalar(145, 243, 208),
    cv::Scalar(2, 184, 176),   cv::Scalar(219, 220, 93),
    cv::Scalar(238, 253, 234), cv::Scalar(197, 169, 160),
    cv::Scalar(204, 201, 106), cv::Scalar(13, 24, 129),
    cv::Scalar(40, 38, 4),     cv::Scalar(5, 41, 34),
    cv::Scalar(46, 94, 129),   cv::Scalar(102, 65, 107),
    cv::Scalar(27, 11, 208),   cv::Scalar(191, 240, 183),
    cv::Scalar(225, 76, 38),   cv::Scalar(193, 89, 124),
    cv::Scalar(30, 14, 175),   cv::Scalar(144, 96, 90),
    cv::Scalar(181, 186, 86),  cv::Scalar(102, 136, 34),
    cv::Scalar(158, 71, 15),   cv::Scalar(183, 81, 247),
    cv::Scalar(73, 69, 89),    cv::Scalar(123, 73, 232),
    cv::Scalar(4, 175, 57),    cv::Scalar(87, 108, 23),
    cv::Scalar(105, 204, 142), cv::Scalar(63, 115, 53),
    cv::Scalar(105, 153, 126), cv::Scalar(247, 224, 137),
    cv::Scalar(136, 21, 188),  cv::Scalar(122, 129, 78),
    cv::Scalar(145, 80, 81),   cv::Scalar(51, 167, 149),
    cv::Scalar(162, 173, 20),  cv::Scalar(252, 202, 17),
    cv::Scalar(10, 40, 3),     cv::Scalar(150, 90, 254),
    cv::Scalar(169, 21, 68),   cv::Scalar(157, 148, 180),
    cv::Scalar(131, 254, 90),  cv::Scalar(7, 221, 102),
    cv::Scalar(19, 191, 184),  cv::Scalar(98, 126, 199),
    cv::Scalar(210, 61, 56),   cv::Scalar(252, 86, 59),
    cv::Scalar(102, 195, 55),  cv::Scalar(160, 26, 91),
    cv::Scalar(60, 94, 66),    cv::Scalar(204, 169, 193),
    cv::Scalar(126, 4, 181),   cv::Scalar(229, 209, 196),
    cv::Scalar(195, 170, 186), cv::Scalar(155, 207, 148)};

int maskDraw(MASK_PARAM_T *param) {
  param->imgData = (unsigned char *)lynPluginGetVirtAddr(param->imgData);
  if (param->imgData == nullptr) {
    LOG_PLUGIN_E("get imgData addr error");
    return -1;
  }

  lynSegBoxesInfo *segBoxesInfo =
      (lynSegBoxesInfo *)lynPluginGetVirtAddr(param->boxInfo);
  if (segBoxesInfo == nullptr) {
    LOG_PLUGIN_E("get targetData addr error\n");
    return -1;
  } else if (segBoxesInfo->boxesNum == 0 || segBoxesInfo->maskData == nullptr) {
    return 0;
  }

  int bytesPerChannel = sizeof(unsigned char);
  int bytesPerRow = param->width * param->channels * bytesPerChannel;
  cv::Mat mat(param->height, param->width, CV_8UC(param->channels),
              param->imgData, bytesPerRow);

  uint8_t *maskDataTmp = (uint8_t *)lynPluginGetVirtAddr(param->maskData);

  size_t offset = 0;
  for (int i = 0; i < segBoxesInfo->boxesNum; i++) {
    lynSegBox *pBox = &segBoxesInfo->boxes[i];
    int col = pBox->maskCol;
    int row = pBox->maskRow;
    cv::Mat boxMask(row, col, CV_8UC1);

    std::memcpy(boxMask.data, maskDataTmp + offset, col * row);
    offset += col * row;

    cv::Rect box(pBox->xmin, pBox->ymin, pBox->width, pBox->height);

    if (row > 0 && col > 0) {
      mat(box).setTo(color80[pBox->id], boxMask);
    }
  }

  return 0;
}

int lynDrawSegBoxAndText(lynDrawSegBoxAndTextPara *para) {
  int ret = 0;
  uint8_t *imgData = (uint8_t *)lynPluginGetVirtAddr(para->imgData);
  lynSegBoxesInfo *boxesInfo =
      (lynSegBoxesInfo *)lynPluginGetVirtAddr(para->boxesInfo);
  for (uint32_t i = 0; i < boxesInfo->boxesNum; i++) {
    auto &box = boxesInfo->boxes[i];

    lynDrawBoxAttr boxAttr = {
        box.xmin,   box.ymin,       box.width,       box.height,     para->imgW,
        para->imgH, para->boxThick, para->boxColor, para->imgFmt, imgData};

    if (box.height - box.ymin <= para->boxThick * 2 || box.width - box.xmin <= para->boxThick * 2) continue;

    ret = lynPluginDrawBox(&boxAttr);
    if (ret != 0) {
      break;
    }
    std::wstring wideString = UTF8ToWstring(box.label);
    lynDrawTextAttrV2 textAttr = {box.xmin,
                                  box.ymin,
                                  para->imgW,
                                  para->imgH,
                                  para->imgFmt,
                                  para->fontSize,
                                  para->fontColor,
                                  imgData,
                                  const_cast<wchar_t *>(wideString.data())};
    ret = lynPluginDrawTextV2(&textAttr);
    if (ret != 0) {
      break;
    }
  }

  return ret;
}